package impl

import (
	"context"

	"gitee.com/liuxiaowei123/vblog-01/vblog-01/vblog/conf"
	"gitee.com/liuxiaowei123/vblog-01/vblog-01/vblog/exception"
	"gitee.com/liuxiaowei123/vblog-01/vblog-01/vblog/ioc"
	"gitee.com/liuxiaowei123/vblog-01/vblog-01/vblog/user"
	"gorm.io/gorm"
)

// 约束对象满足接口
var _ user.Service = &UserServiceImpl{}

// 在导入此包时，会执行init函数
// 通过导入此包的方式，把UserServiceImpl对象注册到ioc中
func init() {
	// 定义一个常量，实现Registry的name
	// ioc.Controller().Registry(user.App,&UserServiceImpl{} )
	// 改进
	ioc.Controller().Registry(&UserServiceImpl{})
}

// 让注册对象UserServiceImpl实现Init()方法，来实现初始化; 所以注释掉NewUserServiceImpl()
func (u *UserServiceImpl) Init() {
	u.db = conf.C.Mysql.Getconn().Debug()
}

// 实现IocObject接口
func (u *UserServiceImpl) Name() string {
	return user.AppName
}

// 定义实现接口的对象
// 它是user Service的控制器，控制着user接口的实现
type UserServiceImpl struct {
	// 依赖数据库
	// 数据库的ip:port是？ 所以要由配置文件提供
	db *gorm.DB
}

// 在程序配置初始化后调用，启动程序时，都会先加载配置的，所以在这里不用操作
// func NewUserServiceImpl() *UserServiceImpl {
// 	return &UserServiceImpl{
// 		// 加入debug模式，可以看到执行的sql语句
// 		db: conf.C.Mysql.Getconn().Debug(),
// 	}
// }

func (u *UserServiceImpl) CreateUser(ctx context.Context, req *user.CreateUserRequest) (*user.User, error) {
	// 1.校验用户参数
	// 因为是创建用户，所以判断是否为空，而不是要把传入的参数和数据库参数做对比
	// 因为用来判断的字段，都在CreateUserRequest中，所以可以构造一个方法来简化代码
	// if  req.Username == "" || req.Passwd == "" {
	// 	return nil,fmt.Errorf("请填写用户名或密码")
	// }
	if err := req.Validate(); err != nil {
		return nil, err
	}

	// 2.创建用户，也就是构造一个结构体的实例
	// 如果用下面代码来创建用户，会有空指针的风险
	/*ins := &user.User{
		Meta: &common.Meta{},
		CreateUserRequest: req,
	}*/
	ins := user.NewUser(req)

	// 3.把用户保存到数据库，通过gorm实现
	// 如何知道要存到user表中？如何和表中的数据完成映射？
	// 通过结构体（User）实现TableName(); `grom:"column:username"`实现映射关系
	// 如果由于数据库响应慢，用户取消操作，数据还会保存吗？  --还会保存的，如何实现取消后不保存呢？
	// u.db.Create(ins)  这样写的话，即使username唯一键冲突，也不会报错的
	if err := u.db.WithContext(ctx).Create(ins).Error; err != nil {
		return nil, err
	}

	// 4.返回User
	return ins, nil
}

func (u *UserServiceImpl) DeleteUser(ctx context.Context, req *user.DeleteUserRequest) error {
	// 加入DescirbeUser逻辑
	a := user.NewDescribeUserRequestById(req.IdString())
	_, err := u.DescirbeUser(ctx, a)
	if err != nil {
		return err
	}

	// 通过grom  删除数据库中的用户
	return u.db.WithContext(ctx).Where("id = ?", req.Id).Delete(&user.User{}).Error
}

func (u *UserServiceImpl) DescirbeUser(ctx context.Context, req *user.DescirbeUserRequest) (*user.User, error) {
	// 通过gorm来查询用户
	query := u.db.WithContext(ctx)

	// 构造出一个用于查询的实例
	ins := user.NewUser(user.NewCreateUserRequest())

	switch req.DescribeBy {
	case user.DESCRIBE_BY_ID:
		query = query.Where("id = ?", req.DescribeValue)
	case user.DESCRIBE_BY_USERNAME:
		query = query.Where("username = ?", req.DescribeValue)
	}

	if err := query.First(ins).Error; err != nil {
		// gorm中自带异常de变量，ErrRecordNotFound = errors.New("record not found")
		// 判断过后才能把这个异常改为自定义的业务异常
		// 有必须做断言嘛？？？
		if err == gorm.ErrRecordNotFound {
			return nil, exception.NewNotFound("user %s not found", req.DescribeValue)
		}
		return nil, err
	}

	// 使用find时，即使查不到数据，也不会报错的
	/*if err := query.Find(ins).Error; err != nil {
		return nil, err
	}*/

	return ins, nil
}
